package com.hivekion.quartz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hivekion.common.entity.PagedResultVo;
import com.hivekion.common.exception.BusinessException;
import com.hivekion.quartz.domain.JobEntity;
import com.hivekion.quartz.domain.vo.*;
import com.hivekion.quartz.mapper.JobMapper;
import com.hivekion.quartz.service.IQuartzService;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @authot morning
 * @date 2021/8/4 9:44
 */
@Service
public class QuartzServiceImpl implements IQuartzService {

      private static Logger log = LoggerFactory.getLogger(QuartzServiceImpl.class);
      private static String JOB_STATISTICAL_NAME =
	      "com.hivekion.quartz.taskJob.StatisticalParameterExecuteJob";
      private static String JOB_STATISTICAL_SPLIT_NAME =
	      "com.hivekion.quartz.taskJob.isSplit";
      private static String JOB_SUMMARY_NAME =
	      "com.hivekion.quartz.taskJob.SummaryParameterExecuteJob";

      @Autowired
      @Qualifier("scheduler")
      private Scheduler scheduler;

      @Autowired
      private JobMapper jobMapper;

      @Override
      public List<JobRunViewVo> queryListOpen() {
	    try {
		  Set<JobKey> jobKeySet = scheduler.getJobKeys(GroupMatcher.anyGroup());
		  List<JobRunViewVo> list = jobKeySet
			  .stream()
			  .map(
				  item -> {
					JobRunViewVo vo = new JobRunViewVo();
					try {
					      vo.setNextFireTime(
						      scheduler.getTriggersOfJob(item).get(0).getNextFireTime()
					      );
					      vo.setPreviousFireTime(
						      scheduler.getTriggersOfJob(item).get(0).getPreviousFireTime()
					      );
					      JobDetail jobDetail = scheduler.getJobDetail(item);
					      vo.setRunKey(jobDetail.getKey().getName());
					      vo.setDescription(jobDetail.getDescription());

					      TriggerKey triggerKey = TriggerKey.triggerKey(
						      item.getName(),
						      item.getGroup()
					      );
					      Trigger.TriggerState state = scheduler.getTriggerState(
						      triggerKey
					      );
					      vo.setState(state);
					} catch (SchedulerException e) {
					      e.printStackTrace();
					}
					return vo;
				  }
			  )
			  .collect(Collectors.toList());

		  return list;
	    } catch (Exception ex) {
		  return null;
	    }
      }

      @Override
      public List<JobRunViewVo> queryRuningList() {
	    try {
		  List<JobExecutionContext> jobContexts = scheduler.getCurrentlyExecutingJobs();
		  List<JobRunViewVo> list = jobContexts
			  .stream()
			  .map(
				  item -> {
					JobRunViewVo vo = new JobRunViewVo();
					vo.setNextFireTime(item.getNextFireTime());
					vo.setPreviousFireTime(item.getPreviousFireTime());
					vo.setRunKey(item.getJobDetail().getKey().getName());
					vo.setDescription(item.getJobDetail().getDescription());
					return vo;
				  }
			  )
			  .collect(Collectors.toList());
		  return list;
	    } catch (Exception ex) {
		  return null;
	    }
      }

      @Override
      @Transactional
      public Boolean addJob(JobCreateInputVo job) {
	    JobEntity entity = new JobEntity();
	    BeanUtils.copyProperties(job, entity);
	    // 根据选定进行发送
	    if (job.getIsSplit() == 0) {
		  entity.setClassName(JOB_STATISTICAL_SPLIT_NAME);
	    } else if (job.getIsSplit() == 1) {
		  entity.setClassName(JOB_STATISTICAL_NAME);
	    } else if (job.getIsSplit() == 2) {
		  entity.setClassName(JOB_SUMMARY_NAME);
	    }
	    //entity.setClassName(JOB_CLASS_NAME);
	    // String expression = this.getCronExpression(job.getDateType(), job.getSendTime());
	    jobMapper.insert(entity);
	    String jobId = entity.getId();
	    entity.setJobGroup(getJobGroub(jobId));
	    entity.setTriggerName(getJobTrigger(jobId));
	    entity.setTriggerGroup(getJobTriggerGroup(jobId));
	    jobMapper.updateById(entity);
	    this.startJob(jobId);
	    return true;
      }

      @Override
      public Boolean updateJob(JobUpdateInputVo inputVo) {
	    try {
		  JobEntity jobEntity = jobMapper.selectById(inputVo.getId());
		  if (jobEntity == null) {
			throw new BusinessException(500, "未找到定时任务信息！");
		  }

		  // 判断执行周期和时间是否发生变化，如果发生变化需要更新定时器，如没有发生变化只更新数据库即可
		  if (
			  jobEntity.getDateType() == inputVo.getDateType() &&
				  inputVo.getSendTime().equals(jobEntity.getSendTime())
			  ) {
			BeanUtils.copyProperties(inputVo, jobEntity);
			jobMapper.updateById(jobEntity);
		  } else {
			BeanUtils.copyProperties(inputVo, jobEntity);
			String expression =
				this.getCronExpression(inputVo.getDateType(), inputVo.getSendTime());
			jobEntity.setCronExpression(expression);
			jobMapper.updateById(jobEntity);
			// 更新scheduler
			TriggerKey triggerKey = new TriggerKey(
				jobEntity.getTriggerName(),
				jobEntity.getTriggerGroup()
			);
			CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(
				triggerKey
			);
			CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(
				jobEntity.getCronExpression()
			);
			// 重新构件表达式
			CronTrigger trigger = cronTrigger
				.getTriggerBuilder()
				.withIdentity(triggerKey)
				.withSchedule(cronScheduleBuilder)
				.withDescription(jobEntity.getRemark())
				.build();
			scheduler.rescheduleJob(triggerKey, trigger);
			log.info("定时任务[{}]更新成功", jobEntity.getJobName());
		  }
		  return true;
	    } catch (Exception e) {
		  e.printStackTrace();
	    }
	    return false;
      }

      /*
	  启动定时任务
	   * */
      @Override
      public boolean startJob(String id) {
	    JobEntity job = jobMapper.selectById(id);
	    String jobName = job.getJobName();
	    String jobUnique = job.getId();
	    try {
		  JobDetail jobDetail = JobBuilder
			  .newJob((Class<? extends Job>) Class.forName(job.getClassName())) // 指定执行类
			  .withIdentity(jobUnique, job.getJobGroup()) // 指定name和group
			  .withDescription(jobName + job.getRemark())
			  .requestRecovery()
			  .build();
		  // 创建表达式调度构建器
		  CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(
			  job.getCronExpression()
		  );
		  // 创建触发器
		  CronTrigger cronTrigger = TriggerBuilder
			  .newTrigger()
			  .withIdentity(job.getTriggerName(), job.getTriggerGroup())
			  .withDescription(job.getRemark())
			  .withSchedule(cronScheduleBuilder)
			  .build();
		  scheduler.scheduleJob(jobDetail, cronTrigger);
		  scheduler.start();
		  log.info("定时任务[{}]创建成功", job.getId() + jobName);

		  if (job.getPauseStatus() == 0) {
			scheduler.pauseJob(JobKey.jobKey(jobUnique, job.getJobGroup()));
		  }
	    } catch (Exception ex) {
	    }
	    return true;
      }

      // 执行一次
      @Override
      public Boolean runJob(String id) {
	    try {
		  JobEntity jobEntity = jobMapper.selectById(id);
		  if (jobEntity == null) {
			throw new BusinessException(500, "未找到任务信息");
		  }
		  String jobId = jobEntity.getId();
		  scheduler.triggerJob(JobKey.jobKey(jobId, jobEntity.getJobGroup()));
		  log.info("定时任务[{}]执行成功", jobId);
		  return true;
	    } catch (Exception e) {
		  e.printStackTrace();
	    }
	    return false;
      }

      @Override
      public Boolean pauseJob(String id) {
	    try {
		  JobEntity jobEntity = jobMapper.selectById(id);
		  jobEntity.setPauseStatus(0);
		  jobMapper.updateById(jobEntity);
		  String jobId = jobEntity.getId();
		  String jobName = jobEntity.getJobName();
		  scheduler.pauseJob(JobKey.jobKey(jobId, jobEntity.getJobGroup()));
		  log.info("定时任务[{}]暂停成功", jobName);
		  return true;
	    } catch (Exception e) {
		  e.printStackTrace();
	    }
	    return false;
      }

      @Override
      public Boolean resumeJob(String id) {
	    try {
		  JobEntity jobEntity = jobMapper.selectById(id);
		  jobEntity.setPauseStatus(1);
		  jobMapper.updateById(jobEntity);
		  String jobId = jobEntity.getId();
		  String jobName = jobEntity.getJobName();
		  scheduler.resumeJob(JobKey.jobKey(jobId, jobEntity.getJobGroup()));
		  log.info("定时任务[{}]唤醒成功", jobName);
		  return true;
	    } catch (Exception e) {
		  e.printStackTrace();
	    }
	    return false;
      }

      @Override
      public Boolean deleteJob(String id) {
	    try {
		  JobEntity jobEntity = jobMapper.selectById(id);
		  if (jobEntity == null) {
			throw new BusinessException(500, "未找到任务信息！");
		  }
		  scheduler.deleteJob(
			  JobKey.jobKey(jobEntity.getId(), jobEntity.getJobGroup())
		  );
		  jobMapper.deleteById(id);
		  log.info("定时任务[{}]删除成功", jobEntity.getJobName());
		  return true;
	    } catch (Exception e) {
		  e.printStackTrace();
	    }
	    return false;
      }

      @Override
      public Boolean removes(List<String> ids) {
	    ids.forEach(item -> deleteJob(item));
	    return true;
      }

      @Override
      public PagedResultVo<JobViewVo> queryList(JobSearchPageVo jobEntity) {
	    LambdaQueryWrapper<JobEntity> queryWrapper = Wrappers.lambdaQuery();
	    if (StringUtils.isNotBlank(jobEntity.getJobName())) {
		  queryWrapper.like(JobEntity::getJobName, jobEntity.getJobName());
	    }
	    Page<JobEntity> jobPage = new Page<>(
		    jobEntity.getPageNum(),
		    jobEntity.getPageSize()
	    );
	    IPage<JobViewVo> jobs = jobMapper
		    .selectPage(jobPage, queryWrapper)
		    .convert(
			    item -> {
				  JobViewVo vo = new JobViewVo();
				  BeanUtils.copyProperties(item, vo);
				  return vo;
			    }
		    );
	    Integer total = (int) jobs.getTotal();
	    PagedResultVo<JobViewVo> resultVo = new PagedResultVo<JobViewVo>(
		    jobEntity,
		    total,
		    jobs.getRecords()
	    );
	    return resultVo;
      }

      @Override
      public JobModelVo getInfo(String id) {
	    JobEntity entity = jobMapper.selectById(id);
	    if (entity == null) {
		  throw new BusinessException(500, "未找到定时任务");
	    }
	    JobModelVo vo = new JobModelVo();
	    BeanUtils.copyProperties(entity, vo);
	    return vo;
      }

      /**
       * 生成触发器组
       *
       * @param
       * @return
       */
      private String getJobTriggerGroup(String id) {
	    return id + "_trigger_Group";
      }

      /**
       * 生成触发器name
       *
       * @param
       * @return
       */
      private String getJobTrigger(String id) {
	    return id + "_trigger";
      }

      /**
       * 生成job组
       *
       * @param
       * @return
       */
      private String getJobGroub(String id) {
	    return id + "_Group";
      }

      private String getCronExpression(int dateType, Date sendTime) {
	    String ss = sendTime.getSeconds() + "";
	    String mm = sendTime.getMinutes() + "";
	    String hh = sendTime.getHours() + "";

	    String expression = "";
	    switch (dateType) {
		  case 0: // 天
			expression = ss + " " + mm + " " + hh + " * * ?";
			break;
		  case 1: // 周
			expression = ss + " " + mm + " " + hh + " ? 0 1 *";
			break;
		  case 2: // 月
			expression = ss + " " + mm + " " + hh + " ? 1 ? *";
			break;
		  case 3: // 季度
			expression = ss + " " + mm + " " + hh + " ? 1/3 ? *";
			break;
		  case 4: // 年
			expression = ss + " " + mm + " " + hh + " ? ? *";
			break;
		  case 5: // 秒
			expression = ss + " * * * * ? ";
			break;
		  case 6: // 分钟
			expression = ss + " " + mm + " * * * ? ";
			break;
		  case 7: // 小时
			expression = ss + " " + mm + " " + hh + " * * ?";
			break;
	    }
	    return expression;
      }
}
